package com.xr.system.student.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xr.common.redis.RedisCacheService;
import com.xr.common.utils.XrObjectUtil;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import com.xr.common.axios.AxiosEnum;
import com.xr.common.axios.AxiosResult;
import com.xr.common.exception.MyCustomException;
import com.xr.system.student.mapper.StudentMapper;
import com.xr.system.model.student.pojo.Student;
import com.xr.system.model.student.dto.StudentDto;
import com.xr.system.student.service.IStudentService;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 学生信息表
 *
 * @author fzh
 * @email e_341877120_mail@163.com
 * @date 2022-09-29 11:13:09
 */
@Service
@Transactional
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements IStudentService {

    @Resource
    private RedisCacheService redisCacheService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 根据条件查询列表
     *
     * @param studentDto
     * @return
     */
    @Override
    public AxiosResult selectList(StudentDto studentDto) {
//        LambdaQueryWrapper<Student> lqw;
//        if (ObjectUtils.isEmpty(studentDto)) {
//            lqw = null;
//        } else {
//            lqw = new LambdaQueryWrapper<>();
//            lqw.like(ObjectUtils.isNotEmpty(studentDto.getName()), Student::getName, studentDto.getName());
//        }

//        String key = "student:cache";
//        Map<Object, Object> cacheMap;
//        Student studentCache;
//        Map<String, Object> map = new HashMap<>();
//        long length = this.count(lqw);
//        if (length > 0) {
//            for (int i = 0; i < length; i++) {
//                // 先查询Redis中是否有缓存数据，命中，直接返回
//                if (ObjectUtils.isNotEmpty(stringRedisTemplate.opsForHash().values(key + 0))) {
//                    cacheMap = stringRedisTemplate.opsForHash().entries(key + i);
////                    studentCache = BeanUtil.fillBeanWithMap(cacheMap, new Student(), true);
////                    ObjectMapper objectMapper = new ObjectMapper();
////                    studentCache = objectMapper.convertValue(cacheMap, Student.class);
//                    studentCache = (Student) ObjectConverter.mapToJavaBean(cacheMap, Student.class);
//                    map.put(key + i, studentCache);
//                }
//            }
//            if (ObjectUtils.isNotEmpty(map) && map.size() > 0) {
//                List<Object> list = new ArrayList<>(map.values());
//                return AxiosResult.selectSuccess(list);
//            }
//        }
//
//        // 未命中，查询数据库返回，并存入Redis
//        List<Student> studentList = baseMapper.selectList(lqw);
//        int j = 0;
//        for (Student student : studentList) {
//            Map<String, Object> studentMap = BeanUtil.beanToMap(
//                    student, new HashMap<>(),
//                    CopyOptions.create()
//                            // 自定义字段类型转换规则，解决 例如主键id的Long类型转换为Redis的hash结构存储所需要的String类型 等诸多类型转换问题
//                            .setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString())
//            );
//            stringRedisTemplate.opsForHash().putAll(key + j, studentMap);
//            j++;
//        }
//        boolean flag = ObjectUtils.isNotEmpty(studentList);
//        return flag ? AxiosResult.selectSuccess(studentList) : AxiosResult.selectError();

        LambdaQueryWrapper<Student> lqw;
        if (XrObjectUtil.checkOfAllNull(studentDto)) {
            lqw = null;
        } else {
            lqw = new LambdaQueryWrapper<>();
            lqw.like(ObjectUtils.isNotEmpty(studentDto.getName()), Student::getName, studentDto.getName());
        }

//        List<Student> studentList = redisCacheClient.queryListFromRedisIfExists("student_cache_", Student.class, lqw, this::count, this::count, this::list, this::getIdList);
//        return AxiosResult.selectSuccess(studentList);

        // Redis缓存优化方案
        List<Student> studentList = redisCacheService.queryAllFromRedisIfExists(
                "student:cache", 86400L, TimeUnit.SECONDS, Student.class, lqw, this::list
        );
        return AxiosResult.selectSuccess(studentList);
    }

    /**
     * 通过对象列表获取id集合
     *
     * @param studentList 对象列表
     * @return List<Long> id集合
     */
    public List<Long> getIdList(List<Student> studentList) {
        List<Long> idList = new ArrayList<>();
        for (Student student : studentList) {
            Long id = student.getId();
            idList.add(id);
        }
        return idList;
    }

    /**
     * 根据条件分页查询
     *
     * @param currentPage
     * @param pageSize
     * @param studentDto
     * @return
     */
    @Override
    @Cacheable(value = "student#3600")
    public AxiosResult selectPage(int currentPage, int pageSize, StudentDto studentDto) {
        // 原方案
        IPage<Student> iPage = new Page(currentPage, pageSize);
        LambdaQueryWrapper<Student> lqw;
        if (ObjectUtils.isEmpty(studentDto)) {
            lqw = null;
        } else {
            lqw = new LambdaQueryWrapper<>();
        }
        iPage = page(iPage, lqw);
        boolean flag = ObjectUtils.isNotEmpty(iPage);
        return flag ? AxiosResult.selectSuccess(iPage) : AxiosResult.selectError();

        // Redis缓存优化方案
//        LambdaQueryWrapper<Student> lqw;
//        if (XrObjectUtil.checkOfAllNull(studentDto)) {
//            lqw = null;
//        } else {
//            lqw = new LambdaQueryWrapper<>();
//            lqw.like(ObjectUtils.isNotEmpty(studentDto.getName()), Student::getName, studentDto.getName());
//        }
//        PageUtil pageUtil = redisCacheService.queryByPageFromRedisIfExists(
//                "student:cache", 86400L, TimeUnit.SECONDS, Student.class, currentPage, pageSize, lqw, this::list
//        );
//        return AxiosResult.selectSuccess(pageUtil);
    }

    /**
     * 根据id查询单个
     *
     * @param id
     * @return
     */
    @Override
    public AxiosResult selectOneById(Long id) {
        if (ObjectUtils.isNotEmpty(id)) {
            Student student = getById(id);
            boolean flag = ObjectUtils.isNotEmpty(student);
            return flag ? AxiosResult.selectSuccess(student) : AxiosResult.selectError();
        }
        throw new MyCustomException(AxiosEnum.PARAMETER_ERROR);
    }

    /**
     * 新增
     *
     * @param studentDto
     * @return
     */
    @Override
    public AxiosResult insert(StudentDto studentDto) {
        if (ObjectUtils.isNotEmpty(studentDto)) {
            boolean flag = save(studentDto);
            return flag ? AxiosResult.insertSuccess() : AxiosResult.insertError();
        }
        throw new MyCustomException(AxiosEnum.PARAMETER_ERROR);
    }

    /**
     * 更新
     *
     * @param studentDto
     * @return
     */
    @Override
    public AxiosResult update(StudentDto studentDto) {
        if (ObjectUtils.isNotEmpty(studentDto)) {
            boolean flag = updateById(studentDto);
            return flag ? AxiosResult.updateSuccess() : AxiosResult.updateError();
        }
        throw new MyCustomException(AxiosEnum.PARAMETER_ERROR);
    }

    /**
     * 根据id删除单个
     *
     * @param id
     * @return
     */
    @Override
    public AxiosResult delete(Long id) {
        if (ObjectUtils.isNotEmpty(id)) {
            boolean flag = redisCacheService.deleteWithConsistency("student_cache_", id, this::list, this::removeById, this::getIdList);
//            boolean flag = removeById(id);
            return flag ? AxiosResult.deleteSuccess() : AxiosResult.deleteError();
        }
        throw new MyCustomException(AxiosEnum.PARAMETER_ERROR);
    }

    /**
     * 根据id集合批量删除
     *
     * @param idList
     * @return
     */
    @Override
    public AxiosResult deleteBatch(List<Long> idList) {
        if (ObjectUtils.isEmpty(idList) || idList.size() == 0) {
            throw new MyCustomException(AxiosEnum.PARAMETER_ERROR);
        }
        boolean flag = removeByIds(idList);
        return flag ? AxiosResult.deleteSuccess() : AxiosResult.deleteError();
    }

    /**
     * 导入文件
     *
     * @param file
     * @return
     */
    @Override
    public AxiosResult importFile(MultipartFile file) {
        boolean flag = false;
        try {
            InputStream inputStream = file.getInputStream();
            ExcelReader er = ExcelUtil.getReader(inputStream);
            List<Student> studentList = er.readAll(Student.class);
            flag = saveBatch(studentList);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return flag ? AxiosResult.success(AxiosEnum.IMPORT_SUCCESS) : AxiosResult.error(AxiosEnum.IMPORT_ERROR);
    }

    /**
     * 导出文件
     *
     * @param response
     */
    @Override
    public void exportFile(HttpServletResponse response) {
        try {
            List<Student> studentList = list();
            ExcelWriter ew = ExcelUtil.getWriter(true);
            ew.setOnlyAlias(true);
            ew.write(studentList);
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            String fileName = URLEncoder.encode("Excel导出表", "UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
            ServletOutputStream sos = response.getOutputStream();
            ew.flush(sos, true);
            sos.close();
            IoUtil.close(sos);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
